package com.gitee.feizns.quickstart.web.spring.boot.autoconfigure.ex;

import com.gitee.feizns.quickstart.web.ex.ExceptionHandler;
import com.gitee.feizns.quickstart.web.ex.GlobalRestfulExceptionAdvice;
import com.gitee.feizns.quickstart.web.ex.impl.BadRequestExceptionHandler;
import com.gitee.feizns.quickstart.web.ex.impl.BindExceptionHandler;
import com.gitee.feizns.quickstart.web.ex.impl.ConstraintViolationExceptionHandler;
import com.gitee.feizns.quickstart.web.ex.impl.DecryptionAlgorithmDecryptionFailedExceptionHandler;
import com.gitee.feizns.quickstart.web.ex.impl.HttpExceptionHandler;
import com.gitee.feizns.quickstart.web.ex.impl.HttpMediaTypeNotSupportedExceptionHandler;
import com.gitee.feizns.quickstart.web.ex.impl.HttpMessageNotReadableExceptionHandler;
import com.gitee.feizns.quickstart.web.ex.impl.HttpRequestMethodNotSupportedExceptionHandler;
import com.gitee.feizns.quickstart.web.ex.impl.MethodArgumentNotValidExceptionHandler;
import com.gitee.feizns.quickstart.web.ex.impl.NotFoundExceptionHandler;
import com.gitee.feizns.quickstart.web.ex.impl.ServiceExceptionHandler;
import lombok.RequiredArgsConstructor;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.RequestBody;

import javax.validation.Valid;

/**
 * 全局异常配置
 * @author feizns
 * @since 2019/6/13
 */
@Configuration
@RequiredArgsConstructor
@EnableConfigurationProperties(GlobalExceptionProperties.class)
public class GlobalExceptionAutoConfiguration {

    /**
     * 属性
     */
    private final GlobalExceptionProperties properties;

    /**
     * 全局异常处理器配置（将异常链中的异常也进行分发到{@link ExceptionHandler}中）
     * @see GlobalRestfulExceptionAdvice
     * @param customizeExceptionHandlers {@link ExceptionHandler}
     * @return {@link GlobalRestfulExceptionAdvice}
     */
    @Bean
    @ConditionalOnMissingBean
    public GlobalRestfulExceptionAdvice globalRestfulExceptionAdvice(ExceptionHandler<? extends Exception>[] customizeExceptionHandlers) {
        GlobalRestfulExceptionAdvice globalRestfulExceptionAdvice = new GlobalRestfulExceptionAdvice(properties.isDebug());
        for (ExceptionHandler<?> handler : customizeExceptionHandlers) {
            for (Class<? extends Throwable> support : handler.support())
                globalRestfulExceptionAdvice.registryCustomizeExceptionHandler(support, handler);
        }
        return globalRestfulExceptionAdvice;
    }

    /*
      =======================================================================
      ======================默认为全局异常解析器配置的异常处理器====================
      =======================================================================
     */

    /**
     * 业务异常处理器
     * @return {@link ServiceExceptionHandler}
     */
    @Bean
    @ConditionalOnMissingBean
    @ConditionalOnBean(GlobalRestfulExceptionAdvice.class)
    public ServiceExceptionHandler serviceExceptionHandler() {
        return new ServiceExceptionHandler();
    }

    /*
      ==============================================================================
      ============================HTTP状态码异常处理器=================================
      ==============================================================================
     */

    /**
     * HTTP异常响应处理器
     * @return {@link HttpExceptionHandler}
     */
    @Bean
    @ConditionalOnMissingBean
    @ConditionalOnBean(GlobalRestfulExceptionAdvice.class)
    public HttpExceptionHandler httpExceptionHandler() {
        return new HttpExceptionHandler();
    }

    /*
      ==============================================================================
      对于 POST PUT 类请求的时候，在使用对 @RequestBody
      请求体参数进行验证的时候，一般需要在实体类前面添加 使用 @Valid 或者 @Validated（两者都可以）
      ==============================================================================
     */

    /**
     * 数据校验异常处理器
     * @return {@link BindExceptionHandler}
     */
    @Bean
    @ConditionalOnMissingBean
    @ConditionalOnBean(GlobalRestfulExceptionAdvice.class)
    public BindExceptionHandler bindExceptionHandler() {
        return new BindExceptionHandler(properties.getValidationFailHttpStatus());
    }

    /**
     * 数据校验异常处理器
     * 对于 POST PUT 类请求的时候，在使用对 {@link RequestBody}
     * 请求体参数进行验证的时候，一般需要在实体类前面添加注解
     * {@link Validated} 或者 {@link Valid}（两者都可以）
     * @see ExceptionHandler
     * @return {@link MethodArgumentNotValidExceptionHandler}
     */
    @Bean
    @ConditionalOnMissingBean
    @ConditionalOnBean(GlobalRestfulExceptionAdvice.class)
    public MethodArgumentNotValidExceptionHandler methodArgumentNotValidExceptionHandler() {
        return new MethodArgumentNotValidExceptionHandler(properties.getValidationFailHttpStatus());
    }

    /**
     * 数据校验异常处理器
     * @return {@link ConstraintViolationExceptionHandler}
     */
    @Bean
    @ConditionalOnMissingBean
    @ConditionalOnBean(GlobalRestfulExceptionAdvice.class)
    public ConstraintViolationExceptionHandler constraintViolationExceptionHandler() {
        return new ConstraintViolationExceptionHandler(properties.getValidationFailHttpStatus());
    }

    /**
     * POST请求体的内容仅通过 HttpMessage 没办法读取的的情况
     * @return {@link HttpMessageNotReadableExceptionHandler}
     */
    @Bean
    @ConditionalOnMissingBean
    @ConditionalOnBean(GlobalRestfulExceptionAdvice.class)
    public HttpMessageNotReadableExceptionHandler httpMessageNotReadableExceptionHandler() {
        return new HttpMessageNotReadableExceptionHandler(properties.getValidationFailHttpStatus());
    }

    /**
     * 请求方法不接受异常（一般是没有相应的控制器）
     * @return {@link HttpRequestMethodNotSupportedExceptionHandler}
     */
    @Bean
    @ConditionalOnMissingBean
    @ConditionalOnBean(GlobalRestfulExceptionAdvice.class)
    public HttpRequestMethodNotSupportedExceptionHandler httpRequestMethodNotSupportedExceptionHandler() {
        return new HttpRequestMethodNotSupportedExceptionHandler(properties.getValidationFailHttpStatus());
    }

    /**
     * 媒体类型不支持异常
     * @return {@link HttpMediaTypeNotSupportedExceptionHandler}
     */
    @Bean
    @ConditionalOnMissingBean
    @ConditionalOnBean(GlobalRestfulExceptionAdvice.class)
    public HttpMediaTypeNotSupportedExceptionHandler httpMediaTypeNotSupportedExceptionHandler() {
        return new HttpMediaTypeNotSupportedExceptionHandler(properties.getValidationFailHttpStatus());
    }

    /**
     * 找不到资源异常处理程序
     * @return {@link NotFoundExceptionHandler}
     */
    @Bean
    @ConditionalOnMissingBean
    @ConditionalOnBean(GlobalRestfulExceptionAdvice.class)
    public NotFoundExceptionHandler notFoundExceptionHandler() {
        return new NotFoundExceptionHandler(properties.getNotFoundHttpExceptionHttpStatus());
    }

    /**
     * 请求参数异常处理程序
     * @return {@link NotFoundExceptionHandler}
     */
    @Bean
    @ConditionalOnMissingBean
    @ConditionalOnBean(GlobalRestfulExceptionAdvice.class)
    public BadRequestExceptionHandler badRequestExceptionHandler() {
        return new BadRequestExceptionHandler(properties.getValidationFailHttpStatus());
    }

    /**
     * 解密失败处理器
     * @return {@link DecryptionAlgorithmDecryptionFailedExceptionHandler}
     */
    @Bean
    @ConditionalOnMissingBean
    @ConditionalOnBean(GlobalRestfulExceptionAdvice.class)
    public DecryptionAlgorithmDecryptionFailedExceptionHandler decryptionAlgorithmDecryptionFailedExceptionHandler() {
        return new DecryptionAlgorithmDecryptionFailedExceptionHandler(properties.getValidationFailHttpStatus());
    }

}
